package com.sdwl.brcl.handler;

import com.google.common.collect.ImmutableMap;
import com.mysql.cj.jdbc.exceptions.MysqlDataTruncation;
import com.sdwl.brcl.exception.NoteException;
import com.sdwl.brcl.util.ApiHttpCode;
import com.sdwl.brcl.validation.exception.ValidationException;
import com.sdwl.brcl.view.ServerResponse;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.HttpHostConnectException;
import org.apache.poi.poifs.filesystem.OfficeXmlFileException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.concurrent.atomic.AtomicReference;

import static com.sdwl.brcl.util.ApiHttpCode.*;

/**
 * 统一异常返回
 *
 * @version v1.0
 * @className: GlobalExceptionHandler
 * @author: heyiheng
 * @date: 2020-11-28 09:33
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

	/**
	 * 定义map，配置异常类型所对应的错误代码
	 */
	private static ImmutableMap<Class<? extends Throwable>, ApiHttpCode> ERROR_MAP;
	/**
	 * 定义map的builder对象，去构建ImmutableMap
	 */
	protected static ImmutableMap.Builder<Class<? extends Throwable>, ApiHttpCode> builder = ImmutableMap.builder();

	//定义异常类型所对应的错误代码
	static {
		//算术异常类
		builder.put(ArithmeticException.class, EX_ARITHMETICEXCEPTION);
		//空指针异常类
		builder.put(NullPointerException.class, EX_NULLPOINTEREXCEPTION);
		//类型强制转换异常
		builder.put(ClassCastException.class, EX_CLASSCASTEXCEPTION);
		//数组下标越界异常
		builder.put(ArrayIndexOutOfBoundsException.class, EX_ARRAYINDEXOUTOFBOUNDSEXCEPTION);
		//输入输出异常
		builder.put(IOException.class, EX_IOEXCEPTION);
		//类循环依赖错误
		builder.put(ClassCircularityError.class, EX_CLASSCIRCULARITYERROR);
		//字符串转换为数字异常
		builder.put(NumberFormatException.class, EX_NUMBERFORMATEXCEPTION);
		//操作数据库异常
		builder.put(SQLException.class, EX_SQLEXCEPTION);
		//索引越界异常
		builder.put(IndexOutOfBoundsException.class, EX_INDEXOUTOFBOUNDSEXCEPTION);
		//接口超时
		builder.put(HttpHostConnectException.class, EX_HTTPHOSTCONNECTEXCEPTION);
		//字符串索引越界异常
		builder.put(StringIndexOutOfBoundsException.class, EX_STRINGINDEXOUTOFBOUNDSEXCEPTION);
		//数据库数据长度大于设置的长度
		builder.put(DataIntegrityViolationException.class, EX_DataIntegrityViolationException);
		//文件后缀的格式和文件本身的格式不匹配
		builder.put(OfficeXmlFileException.class, EX_OFFICEXMLFILEEXCEPTION);
		//数据库数据长度大于设置的长度
		builder.put(MysqlDataTruncation.class, EX_DataIntegrityViolationException);
		builder.put(ConnectTimeoutException.class,EX_CONNECT_TIMEOUT_Exception);

	}

	@ResponseBody
	@ExceptionHandler(value = {Exception.class})
	public Object handleNoteException(HttpServletRequest request, Exception e) {

		if (e instanceof ValidationException) {
			return ServerResponse.createByErrorCodeMessage(EX_CHECK_ARGS.getCode(), e.getMessage());
		}
     	//Validated参数校验
		if (e instanceof ConstraintViolationException) {
			ConstraintViolationException b = (ConstraintViolationException) e;
			return ServerResponse.createByErrorCodeMessage(EX_CHECK_ARGS.getCode(), b.getMessage());
		}
		if (e instanceof MethodArgumentNotValidException) {
			AtomicReference<String> errorMessage = new AtomicReference<>("");
			MethodArgumentNotValidException b = (MethodArgumentNotValidException) e;
			b.getBindingResult().getAllErrors().stream().findFirst().ifPresent((error)->{
				errorMessage.set(error.getDefaultMessage());
			});
			return ServerResponse.createByErrorCodeMessage(EX_CHECK_ARGS.getCode(), errorMessage.get());
		}
		if (e instanceof BindException) {
			//这里为了防止提示过多的信息，选择返回第一个不通过的字段信息
			AtomicReference<String> errorMessage = new AtomicReference<>("");
			BindException b = (BindException) e;
			b.getBindingResult().getAllErrors().stream().findFirst().ifPresent((error)->{
				errorMessage.set(error.getDefaultMessage());
			});
			return ServerResponse.createByErrorCodeMessage(EX_CHECK_ARGS.getCode(), errorMessage.get());
		}

		if (e instanceof NoteException) {
			int code = ((NoteException) e).getCode();
			if (code != 0) {
				return ServerResponse.createByErrorCodeMessage(code,e.getMessage());
			}
			return ServerResponse.createByErrorMessage(e.getMessage());
		}
		if (e instanceof HttpRequestMethodNotSupportedException){
			return ServerResponse.createByErrorMessage("不接受此类请求!");
		}
		e.printStackTrace();
		return ServerResponse.createByErrorMessage("服务器繁忙请稍后再试!");
	}
}
